<template>
    <el-form
        ref="formRef"
        :model="state.form"
        :label-position="state.config.labelAlign"
        :label-width="state.config.labelWidth || '110px'"
        class="z-form">
        <el-row :gutter="26">
            <el-col
                v-for="(item, index) in state.formItems.filter((item: any) => [undefined, false].includes(item.hide))"
                :key="index"
                :xs="24"
                :sm="item.span || state.config.span || 24"
                :md="item.span || state.config.span || 24"
                :lg="item.span || state.config.span || 24"
                :xl="item.span || state.config.span || 24"
                class="mb20">
                <el-form-item
                    :label="['button', 'button-group'].includes(item.ui) || !item.label ? '' : item.label + ':'"
                    :prop="item.key"
                    :label-width="item.labelWidth || undefined"
                    :label-position="item.labelAlign"
                    :rules="formatRules(item.rules)"
                    :class="[item.align === 'right' ? 'form-item-right' : '']">
                    <UI
                        :item="item"
                        :initData="state.initData"
                        :form="state.form"
                        :items="state.formItems"
                        @event="($event) => handleEvent({...$event, formEl: formRef})">
                    </UI>
                </el-form-item>
            </el-col>
        </el-row>
    </el-form>
</template>

<script lang="ts">
import { UIName } from './UI.vue'

export interface Options {
    config?: Config;
    body: UIProps[];
}
export enum Action {
    formCancel = 'formCancel', // 弹窗表单操作
    formSubmit = 'formSubmit', // 弹窗表单操作
}
export interface Config {
    log?: boolean // 是否打印日志
    view?: boolean // 查看模式 (需要组件支持disabled属性)
    initData?: InitData // 初始化数据
    data?: { [key: string]: any } | ((p: any) => any) // 表单数据
    serialization?: boolean // 序列化执行（在异步请求参数时，能保证先后顺序）
    labelWidth?: number | string // 标签宽度
    labelAlign?: string // 标签对齐
    span?: number | string // 栅格 最大24等分
    submitAlign?: SubmitAlign // 提交按钮对齐方式
    submitText?: string // 提交按钮文字
    submitFetch?: Fetch // 提交请求配置
    customFooter?: boolean // 是否自定义提交按钮
    parentUIName?: string // 父组件名称， 用于判断该组件是否被嵌入到其它组件中
    parentData?: any
    parentInitData?: any
}
// 目前只写了按钮和按钮组（其它组件还需完善!!!!!!!!）
export interface UIProps extends ButtonGroupProps, ButtonProps, BindProps, FormProps {
    label?: string
    ui: `${UIName}`
    labelWidth?: string // 隐藏标签宽度
    render?: (p: any) => any // 使用vue 的h函数渲染组件
    dict?: Dict
    on?: BindOn
    disabled?: boolean | ((p: any) => boolean) // 内置组件都需要支持该属性
    [key: string]: any
}
// 表单属性
// required 必填校验
// max开头 最大长度限制 例如：max20 max40
// 自定义校验（element plus规则）
// 远程重复校验
export interface FormProps {
    rules?: any[]
}
// 按钮组
interface ButtonGroupProps {
    align?: SubmitAlign // 对齐方式 right
    children?: ButtonForm[]
}
export enum Align {
    left = 'left',
    right = 'right',
}
type SubmitAlign = `${Align}`
// 按钮
interface ButtonForm extends ButtonProps, ActionProps, BindProps {}
// 按钮属性
export interface ButtonProps {
    label?: string | ((p: any) => string)
    link?: string
    type?: string | ((p: any) => string)
    icon?: string | ((p: any) => string)
    disabled?: boolean | ((p: any) => boolean)
    size?: string
    hide?: boolean | ((p: any) => boolean)
    loading?: boolean
}
// 内置操作
interface ActionProps {
    actionName?: `${Action}` 
    fetch?: Fetch // 请求配置
}
// 绑定事件
export interface BindProps {
    on?: {
        [key: string]: (options: any) => void
    }
}
export interface Fetch {
    validate?: (p: any)=> boolean // 校验
    before?: (p: any)=> any
    api: (p?: any)=> any
    success?: (p: any)=> any
    complete?: (p: any)=> void
    fail?: (p: any)=> void
}
// 字典
export interface Dict {
    data: any[] | ((p: DictParams) => any)
    labelKey?: string
    valueKey?: string
}
interface DictParams {
    form: any
    initData: InitData
}
export interface InitData {
    [key: string]: ((p: InitDataParams) => any) | number | string | boolean | any[]
}
interface InitDataParams {
    form: any
    initData: InitData
}
export interface BindOn {
    [key: string]: (p: any) => void
}

</script>

<script setup lang="ts">
import { ref, reactive, provide, type PropType, onBeforeMount, watch } from 'vue'
import UI from './UI.vue'
import { isFunc, msg, eventCenter, log, setInitData } from './utils'
import useFormRule from './hooks/useFormRule'
const props = defineProps({
    options: {
        type: Object as PropType<Options>,
        default: () => ({})
    }
})
const state = reactive({
    initData: {}, // 初始化数据
    form: {}, // 表单
    formItems: [] as any[], // 表单项
    config: {} as any // 表单配置
})
const emits = defineEmits(['closed'])
const { formatRules, formRef } = useFormRule(state)

const onCancel = ({ item }: any) => {
    emits('closed')
}

const onSubmit = async ({ formEl, item }: any) => {
    if (!formEl) return;
    await formEl.validate(async (valid: boolean) => {
        if (!valid) return;
        // if (kk.value && kk.value.value && kk.value.value.validate) await kk.value.value.validate()
        if (!item.fetch) return log(`"${item.label}"按钮`, '缺少 fetch 配置', 'warn')
        if (!item.fetch.api) return log(`"${item.label}"按钮`, '缺少 fetch.api 配置', 'warn')
        if (item.fetch.validate && !item.fetch.validate({ form: {...state.form}, item, msg })) return
        let p = {...state.form} // 默认传值
        item.loading = true
        if (item.fetch.before) p = item.fetch.before({ form: {...state.form}, item })
        try {
            const { data } = await item.fetch.api(p)
            if (item.fetch.success && isFunc(item.fetch.success)) item.fetch.success({ data, item, msg })
            if (item.fetch.complete && isFunc(item.fetch.complete)) item.fetch.complete({ msg })
            item.loading = false
            emits('closed', data || {})
        } catch (error) {
            item.loading = false
            if (item.fetch.fail && isFunc(item.fetch.fail)) item.fetch.fail(error)
        }
    })
}

const action: any = {
    formCancel: onCancel,
    formSubmit: onSubmit,
}

const handleEvent = (params: any) => {
    eventCenter({...params, items: [...state.formItems]}, action)
}

const createFooter = (fetch: Fetch, text = '保存', align?: SubmitAlign) => {
    const footer: UIProps[] = [
        {
            ui: 'button-group',
            align,
            children: [
                {
                    label: text,
                    type: 'primary',
                    actionName: Action.formSubmit,
                    fetch
                },
                ...props.options.config && props.options.config.parentUIName === 'DialogForm'
                    ?  [{
                        label: '取消',
                        actionName: Action.formCancel
                    }]
                    : []
            ]
        }
    ]
    return footer
}

// 设置表单数据
const setFormData = () => {
    if (!(props.options.config && props.options.config.data)) return
    if (isFunc(props.options.config.data)) {
        state.form = Object.assign(state.form, props.options.config.data({ form: state.form, initData: state.initData }))
    } else {
        state.form = props.options.config.data
    }
}

watch(() => state.initData, (n) => {
    setFormData()
}, { deep: true })

watch(() =>state.formItems, (n) => {
    state.formItems = n
}, { deep: true })

onBeforeMount(() => {
    let body: any[] = []
    if (props.options.config) {
        body = [true, undefined].includes(props.options.config.customFooter) && props.options.config.submitFetch
            ? props.options.body.concat(createFooter(props.options.config.submitFetch, props.options.config.submitText, props.options.config.submitAlign))
            : props.options.body
        state.config = props.options.config
        if (props.options.config.parentData) state.form = { ...props.options.config.parentData }
        if (props.options.config.parentInitData) state.initData = { ...props.options.config.parentInitData }
        if (props.options.config.initData && Object.keys(props.options.config.initData).length){
            setInitData(props.options.config.initData, state.initData, state.form, props.options.config.serialization, props.options.config.log)
        } else {
            setFormData()
        }
        // 查看模式
        if (props.options.config.view === true) body.map(item => item.disabled = true)
    } else {
        body = props.options.body
    }
    // 排序处理
    state.formItems = body.some(item => item.sort)
        ? body.sort((a, b) => a.sort - b.sort)
        : body
})
</script>

<style lang="scss" scoped>
.form-item-right{
    :deep(.el-form-item__content) {
        justify-content: flex-end;
    }
}
.z-form{
    :deep(.el-form-item) {
        margin-bottom: 13px;
        line-height: 1;
    }
    :deep(.el-form-item__error) {
        padding-top: 0;
    }
}
</style>